import {forIn, isArray, isEmpty, isObject, isString, map} from 'lodash-es';

declare type Obj = {
    [x: string]: any
};
declare type ArrayObj = Array<Obj>;

/**
 * 递归为对象数组里的对象，添加 key 字段以及其他额外字段
 * @param data Array<Object>
 * @param keyAccordName String 默认是'id'；为对象添加的'key'字段，是参照对象里哪个原有的字段 作为值的
 * @param addFields Array<Array(2)> 需要添加的字段信息，是个二维数组，例如：[['name', 'title'], ['code', 'keyCode']]，表示添加的'title'字段值为’name'字段的值，以此类推
 * @returns {Array}
 */
export const recurAddKeyAndFileds = (data: ArrayObj, keyAccordName = 'id', addFields = []) => {
    return recursiveMap(data, (obj: Obj, k: any, pObj: Obj) => {
        const reObj = {...obj};

        if (pObj && pObj[keyAccordName]) {
            reObj.key = `${pObj.key || pObj[keyAccordName]}-${obj[keyAccordName]}`;
        } else {
            reObj.key = `${obj[keyAccordName]}`;
        }

        addFields && addFields.forEach(fields => {

            if (fields && fields[0] && fields[1] && reObj && reObj[fields[0]]) {
                reObj[fields[1]] = reObj[fields[0]];
            }
        });
        return reObj;
    });
};

export const recurRemoveFields = (data: ArrayObj, rmFields = ['key']) => {
    return recursiveMap(data, (obj: Obj) => {
        const reObj = {...obj};
        rmFields && rmFields.forEach(fName => {
            delete reObj[fName];
        });
        return reObj;
    });
};

export const recursiveMap = (data: ArrayObj, func: Function, parentObj = {}, childKeyName = 'children', parentInd: any = null) => {

    if (isArray(data) && data.length > 0) {

        const reArr: any = [];
        data.map((obj, key) => {
            const tmpObj = func(obj, key, parentObj, parentInd);

            if (isObject(obj) && tmpObj) {

                // @ts-ignore
                if (!isEmpty(obj[childKeyName])) {
                    // @ts-ignore
                    tmpObj[childKeyName] = recursiveMap(obj[childKeyName], func, tmpObj, childKeyName, key);
                }
            }
            if (false !== tmpObj) {
                reArr.push(tmpObj);
            }
        });
        return reArr;

    } else {
        return [];
    }
};

export const recursiveFind = (data: ArrayObj, func: Function, parentObj?: Obj, childKeyName = 'children', parentInd: any = null): any => {

    if (isArray(data) && data.length > 0) {
        let reObj = null;

        for (let key = 0, len = data.length; key < len; key++) {
            const obj = data[key];
            const isOk = func(obj, key, parentObj, parentInd);
            // console.log('isOk',isOk);
            if (!isOk && isObject(obj)) {

                // @ts-ignore
                if (!isEmpty(obj[childKeyName])) {
                    // @ts-ignore
                    reObj = recursiveFind(obj[childKeyName], func, obj, childKeyName, key);

                    if (!isEmpty(reObj)) break;
                }

            } else {
                reObj = obj;
                break;
            }
        }
        return reObj;

    } else {
        return null;
    }
};

export const addListNumber = (data: ArrayObj, page: Obj) => {
    if (page) {
        const preNo = (page.current - 1) * page.size;
        // const dateFields = [];
        // if (list && list.length > 0) {
        //   const obj = list[0];
        //   for (const k in obj) {
        //     if (obj.hasOwnProperty(k) && -1 !== `${k}`.indexOf('Date')) {
        //       dateFields.push(k);
        //     }
        //   }
        // }
        map(data, (val, key) => {
            const obj = {
                key: key,
                no: key + 1 + (isNaN(preNo) ? 0 : preNo),
                ...val,
            };
            data[key] = obj;
            // dateFields.forEach(v => {
            //   obj[v] = datetime2Date(val[v]);
            // });
            // return obj;
        });
    }
    return data;
};

export const sort = (data: any, str = 'asc') => {
    let mp;
    if (data instanceof Array) {
        mp = [...data];
        let temp;
        for (let i = 0; i < mp.length; i++) {
            for (let j = i + 1; j < mp.length + 1; j++) {

                if (str === 'asc' || !str) {
                    if (mp[i] > mp[j]) {
                        temp = mp[i];
                        mp[i] = mp[j];
                        mp[j] = temp;
                    }
                } else if (str === 'desc') {
                    if (mp[i] < mp[j]) {
                        temp = mp[i];
                        mp[i] = mp[j];
                        mp[j] = temp;
                    }
                }
            }
        }
        return mp;
    } else {
        return data;
    }
};

/**
 * 在对象(obj)中，判断属性值是否包含某个字符串(filterValue)，同时可指定查找对象的哪些属性(keys).
 * 例如：isIncludesByKeys({id: 1, name:'明信', addr:'明星大街'}, '明', ['name', 'addr']);
 * @param obj object 待查的对象
 * @param filterValue string 过滤字符串
 * @param keys string[] 指定对象的查找属性，不指定时，默认会查所有的属性
 * @return boolean
 */
export function isIncludesByKeys<T extends IObj>(obj: T, filterValue: string, keys: string[] = []): boolean {
    let isTrue = false;

    if (isEmpty(filterValue)) {
        isTrue = true;
    } else {

        forIn(obj, (val, key) => {

            if (isString(val)) {
                let v = val.toUpperCase().trim();
                let fVal = `${filterValue}`.toUpperCase().trim();
                // console.log(`-1 !== keys.indexOf(key)`, -1 !== keys.indexOf(key));
                // console.log(`-1 !== keys.indexOf(key)`, v.includes(`${fVal}`));

                if (isEmpty(keys) && v.includes(`${fVal}`)) {
                    isTrue = true;
                    return false;

                } else if (!isEmpty(keys) && -1 !== keys.indexOf(key) && v.includes(`${fVal}`)) {
                    isTrue = true;
                    return false;
                }
            }
        });
    }

    return isTrue;
}

/**
 * 根据关键字(keyWord)搜索树状对象数组（data），可指定查找对象的哪些属性（keys）。对象的子树属性名是 children。
 * 例如：filterByTreeData([{id:1, name: '高某', children:[{id:11, name: '高小名', addr: '某小区'}]}], '名', ['name', 'addr'])
 * @param data Array<IObj & {children?: Array<IObj>}>
 * @param keyWord
 * @param keys
 * @return Array<IObj & {children?: Array<IObj>}>
 */
export function filterByTreeData<T extends IObj>(data: T[], keyWord: string, keys: string[]): T[] {
    return data
        .map(item => {

            if (!isEmpty(item.children)) {
                const tmpList = filterByTreeData(item.children, keyWord, keys);

                if (tmpList.length > 0) {
                    return {
                        ...item,
                        children: tmpList,
                    };
                }
            } else {
                if (isIncludesByKeys(item, keyWord, keys)) {
                    return {
                        ...item,
                        children: [],
                    };
                }
            }

            return undefined;
        })
        .filter(item => item) as T[];
}
